/*
 * px-crypt.c
 *		各种加密算法的封装。
 *
 * Copyright (c) 2001 Marko Kreen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation和/或其他材料提供的发行版本中。
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 或业务中断)
 * HOWEVER CAUSED AND ON ANY理论的责任，不论是合同、严格责任还是侵权（包括疏忽或其他）以任何方式引起的
 * 使用本软件，即使在被告知可能性时也不承担任何责任。
 *
 * contrib/pgcrypto/px-crypt.c
 */

#include "postgres.h"

#include "px-crypt.h"
#include "px.h"

static char * fc_run_crypt_des(const char *fc_psw, const char *fc_salt,
			  char *fc_buf, unsigned fc_len)
{
	char	   *fc_res;

	fc_res = px_crypt_des(fc_psw, fc_salt);
	if (fc_res == NULL || strlen(fc_res) > fc_len - 1)
		return NULL;
	strcpy(fc_buf, fc_res);
	return fc_buf;
}

static char * fc_run_crypt_md5(const char *fc_psw, const char *fc_salt,
			  char *fc_buf, unsigned fc_len)
{
	char	   *fc_res;

	fc_res = px_crypt_md5(fc_psw, fc_salt, fc_buf, fc_len);
	return fc_res;
}

static char * fc_run_crypt_bf(const char *fc_psw, const char *fc_salt,
			 char *fc_buf, unsigned fc_len)
{
	char	   *fc_res;

	fc_res = _crypt_blowfish_rn(fc_psw, fc_salt, fc_buf, fc_len);
	return fc_res;
}

struct px_crypt_algo
{
	char	   *id;
	unsigned	id_len;
	char	   *(*crypt) (const char *psw, const char *salt,
						  char *buf, unsigned len);
};

static const struct px_crypt_algo
			px_crypt_list[] = {
	{"$2a$", 4, fc_run_crypt_bf},
	{"$2x$", 4, fc_run_crypt_bf},
	{"$2$", 3, NULL},			/* 不适用 */
	{"$1$", 3, fc_run_crypt_md5},
	{"_", 1, fc_run_crypt_des},
	{"", 0, fc_run_crypt_des},
	{NULL, 0, NULL}
};

char * px_crypt(const char *fc_psw, const char *fc_salt, char *fc_buf, unsigned fc_len)
{
	const struct px_crypt_algo *fc_c;

	for (fc_c = px_crypt_list; fc_c->id; fc_c++)
	{
		if (!fc_c->id_len)
			break;
		if (strncmp(fc_salt, fc_c->id, fc_c->id_len) == 0)
			break;
	}

	if (fc_c->crypt == NULL)
		return NULL;

	return fc_c->crypt(fc_psw, fc_salt, fc_buf, fc_len);
}

/*
 * 盐生成器
 */

struct generator
{
	char	   *name;
	char	   *(*gen) (unsigned long count, const char *input, int size,
						char *output, int output_size);
	int			input_len;
	int			def_rounds;
	int			min_rounds;
	int			max_rounds;
};

static struct generator gen_list[] = {
	{"des", _crypt_gensalt_traditional_rn, 2, 0, 0, 0},
	{"md5", _crypt_gensalt_md5_rn, 6, 0, 0, 0},
	{"xdes", _crypt_gensalt_extended_rn, 3, PX_XDES_ROUNDS, 1, 0xFFFFFF},
	{"bf", _crypt_gensalt_blowfish_rn, 16, PX_BF_ROUNDS, 4, 31},
	{NULL, NULL, 0, 0, 0, 0}
};

int px_gen_salt(const char *fc_salt_type, char *fc_buf, int fc_rounds)
{
	struct generator *fc_g;
	char	   *fc_p;
	char		fc_rbuf[16];

	for (fc_g = gen_list; fc_g->name; fc_g++)
		if (pg_strcasecmp(fc_g->name, fc_salt_type) == 0)
			break;

	if (fc_g->name == NULL)
		return PXE_UNKNOWN_SALT_ALGO;

	if (fc_g->def_rounds)
	{
		if (fc_rounds == 0)
			fc_rounds = fc_g->def_rounds;

		if (fc_rounds < fc_g->min_rounds || fc_rounds > fc_g->max_rounds)
			return PXE_BAD_SALT_ROUNDS;
	}

	if (!pg_strong_random(fc_rbuf, fc_g->input_len))
		return PXE_NO_RANDOM;

	fc_p = fc_g->gen(fc_rounds, fc_rbuf, fc_g->input_len, fc_buf, PX_MAX_SALT_LEN);
	px_memset(fc_rbuf, 0, sizeof(fc_rbuf));

	if (fc_p == NULL)
		return PXE_BAD_SALT_ROUNDS;

	return strlen(fc_p);
}
